package com.rs.game.player;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import com.rs.Settings;
import com.rs.game.Animation;
import com.rs.game.Entity;
import com.rs.game.Graphics;
import com.rs.game.Hit;
import com.rs.game.Hit.HitLook;
import com.rs.game.Region;
import com.rs.game.World;
import com.rs.game.WorldObject;
import com.rs.game.WorldTile;
import com.rs.game.item.FloorItem;
import com.rs.game.npc.NPC;
import com.rs.game.player.skills.SkillExecutor;
import com.rs.game.tasks.WorldTask;
import com.rs.game.tasks.WorldTasksManager;
import com.rs.net.Session;
import com.rs.net.encoders.WorldPacketsEncoder;
import com.rs.utils.SerializableFilesManager;
import com.rs.utils.Utils;

public final class Player extends Entity {

	private static final long serialVersionUID = 2011932556974180375L;
	
	//transient stuff
	private transient String username;
	private transient Session session;
	private transient boolean clientLoadedMapRegion;
	private transient int displayMode;
	private transient int screenWidth;
	private transient int screenHeight;
	private transient InterfaceManager interfaceManager;
	private transient DialogueManager dialogueManager;
	private transient SkillExecutor skillExecutor;
	private transient CoordsEvent coordsEvent;
	private transient ConcurrentHashMap<Object, Object> temporaryAttributes;
	private transient boolean dontUpdateMyPlayer;
	//used for update
	private transient LocalPlayerUpdate localPlayerUpdate;
	private transient LocalNPCUpdate localNPCUpdate;
	//player masks
	private transient PublicChatMessage nextPublicChatMessage;
	private transient ForceMovement nextForceMovement;
	//player stages
	private transient boolean started;
	private transient boolean running;
	
	private transient long packetsDecoderPing;
	private transient boolean resting;
	private transient boolean canPvp;
	private transient long stopDelay; //used for doors and stuff like that
	private transient int musicId;
	private transient long musicDelay;
	
	//saving stuff
	private boolean mining = false;
	private String password;
	private int rights;
	private String displayName;
	private Appearence appearence;
	private Inventory inventory;
	private Equipment equipment;
	private Skills skills;
	private CombatDefinitions combatDefinitions;
	private Prayer prayer;
	private Bank bank;
	private ControlerManager controlerManager;
	private byte runEnergy;
	private boolean allowChatEffects;
	private boolean mouseButtons;
	private int skullDelay;
	private boolean forceNextMapLoadRefresh;
	
	//creates Player and saved classes
	public Player(String password) {
		super(Settings.START_PLAYER_LOCATION);
		setHitpoints(Settings.START_PLAYER_HITPOINTS);
		this.password = password;
		appearence = new Appearence();
		inventory = new Inventory();
		equipment = new Equipment();
		skills = new Skills();
		combatDefinitions = new CombatDefinitions();
		prayer = new Prayer();
		bank = new Bank();
		controlerManager = new ControlerManager();
		runEnergy = 100;
		allowChatEffects = true;
		mouseButtons = true;
		mining = false;
	}
	
	public void init(Session session, String username, int displayMode, int screenWidth, int screenHeight) {
		this.session = session;
		this.username = username;
		this.displayMode = displayMode;
		this.screenWidth = screenWidth;
		this.screenHeight = screenHeight;
		interfaceManager = new InterfaceManager(this);
		dialogueManager = new DialogueManager(this);
		localPlayerUpdate = new LocalPlayerUpdate(this);
		localNPCUpdate = new LocalNPCUpdate(this);
		skillExecutor = new SkillExecutor(this);
		temporaryAttributes = new ConcurrentHashMap<Object, Object>();
		//loads player on saved instances
		appearence.setPlayer(this);
		inventory.setPlayer(this);
		equipment.setPlayer(this);
		skills.setPlayer(this);
		combatDefinitions.setPlayer(this);
		prayer.setPlayer(this);
		bank.setPlayer(this);
		controlerManager.setPlayer(this);
		setDirection(6);
		initEntity();
		packetsDecoderPing = System.currentTimeMillis();
		//inited so lets add it
		World.addPlayer(this);
		World.updateEntityRegion(this);
		System.out.println("Inited Player: "+password+", name: "+username);
	}

	public boolean getMining() {
		return this.mining;
	}

	public void setMining(boolean mining) {
		this.mining = mining;
	}	
	
	public void setSkull() {
		skullDelay = 3000; //30minutes
		appearence.generateAppearenceData();
	}
	
	public boolean hasSkull() {
		return skullDelay > 0;
	}
	
	public void refreshSpawnedItems() {
		for(int regionId : getMapRegionsIds()) {
			CopyOnWriteArrayList<FloorItem> floorItems = World.getRegion(regionId).getFloorItems();
			 if(floorItems == null) 
				 continue;
			 for(FloorItem item : floorItems) {
					if((item.isInvisible() || item.isGrave()) && this != item.getOwner() || item.getTile().getPlane() != getPlane())
						continue;
					getPackets().sendRemoveGroundItem(item);
			 }
		}
		for(int regionId : getMapRegionsIds()) {
			CopyOnWriteArrayList<FloorItem> floorItems = World.getRegion(regionId).getFloorItems();
			 if(floorItems == null) 
				 continue;
			 for(FloorItem item : floorItems) {
					if((item.isInvisible() || item.isGrave()) && this != item.getOwner() || item.getTile().getPlane() != getPlane())
						continue;
					getPackets().sendGroundItem(item);
			 }
		}
	}
	
	public void refreshSpawnedObjects() {
		for(int regionId : getMapRegionsIds()) {
			 CopyOnWriteArrayList<WorldObject> spawnedObjects = World.getRegion(regionId).getSpawnedObjects();
			 if(spawnedObjects == null) 
				 continue;
			 for(WorldObject object : spawnedObjects)
				 if(object.getPlane() == getPlane())
				 getPackets().sendSpawnedObject(object);
		}
	}
	
	//now that we inited we can start showing game
	public void start() {
		loadMapRegions();
		getPackets().sendWindowsPane(378, 1); //loads welcome screen pane
		started = true;
		if(isDead()) {
			run();
			sendDeath(null);
		}
	}
	
	public void stopAll() {
		stopAll(true);
	}
	
	//as walk done clientsided
	public void stopAll(boolean stopWalk) {
		coordsEvent = null;
		closeInterfaces();
		if(stopWalk)
			resetWalkSteps();
		skillExecutor.forceStop();
		combatDefinitions.resetSpells(false);
		if(resting) {
			addStopDelay(3);
			setNextAnimation(new Animation(5748));
			WorldTasksManager.schedule(new WorldTask() {
				@Override
				public void run() {
					setResting(false);
				}
			}, 2);
		}
	}
	
	@Override
	public void reset() {
		super.reset();
		refreshHitPoints();
		skills.restoreSkills();
		combatDefinitions.resetSpecialAttack();
		prayer.closeAllPrayers();
		combatDefinitions.resetSpells(true, true);
		resting = false;
		skullDelay = 0;
		appearence.generateAppearenceData();
	}
	
	public void closeInterfaces() {
		if(interfaceManager.containsScreenInter())
			interfaceManager.closeScreenInterface();
		if(interfaceManager.containsInventoryInter())
			interfaceManager.closeInventoryInterface();
		dialogueManager.finishDialogue();
	}
	
	@Override
	public void loadMapRegions() {
		super.loadMapRegions();
		clientLoadedMapRegion = false;
		
		if(!started) {
			if(isAtDynamicRegion()) {
				getPackets().sendMapRegion();
				forceNextMapLoadRefresh = true;
			}
		}else
			dontUpdateMyPlayer = true;
		if(isAtDynamicRegion())
			getPackets().sendDynamicMapRegion();
		else	
			getPackets().sendMapRegion();
		forceNextMapLoadRefresh = false;
	}

	@Override
	public void processEntity() {
		try {
		if(hasSkull()) {
			skullDelay--;
			if(!hasSkull())
				appearence.generateAppearenceData();
		}
		if(coordsEvent != null && coordsEvent.processEvent(this))
			coordsEvent = null;
		skillExecutor.process();
		prayer.processPrayer();
		controlerManager.process();
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public void processReceivedHits() {
		if(stopDelay > System.currentTimeMillis())
			return;
		super.processReceivedHits();
		
	}
	
	@Override
	public boolean needMasksUpdate() {
		return super.needMasksUpdate() || nextPublicChatMessage != null || nextForceMovement != null;
	}
	
	@Override
	public void resetMasks() {
		super.resetMasks();
		nextPublicChatMessage = null;
		nextForceMovement = null;
		dontUpdateMyPlayer = false;
	}
	
	public void toogleRun(boolean update) {
		super.setRun(!getRun());
		if(update)
			sendRunButtonConfig();
	}
	
	@Override
	public void setRun(boolean run) {
		if(run != getRun()) {
			super.setRun(run);
			sendRunButtonConfig();
		}
	}
	
	public void sendRunButtonConfig() {
		getPackets().sendConfig(173, resting ? 3 : getRun() ? 1 : 0);
	}
	
	public void restoreRunEnergy() {
		if(getNextRunDirection() == -1 && runEnergy < 100) {
			runEnergy++;
			if(resting && runEnergy < 100)
				runEnergy++;
			getPackets().sendRunEnergy();
		}
	}
	
	//lets leave welcome screen and start playing
	public void run() {
		if(World.exiting_start != 0) {
			int delayPassed = (int) ((System.currentTimeMillis()-World.exiting_start) / 1000);
			getPackets().sendSystemUpdate(World.exiting_delay-delayPassed);
		}
		interfaceManager.sendInterfaces();
		getPackets().sendRunEnergy();
		refreshAllowChatEffects();
		refreshMouseButtons();
		sendRunButtonConfig();
		getPackets().sendGameMessage("Welcome to "+Settings.SERVER_NAME+".");
		sendDefaultPlayersOptions();
		checkMultiArea();
		inventory.init();
		equipment.init();
		skills.init();
		combatDefinitions.init();
		prayer.init();
		refreshHitPoints();
		if(musicId > 0)
			setMusicId(musicId);
		running = true;
		appearence.generateAppearenceData();
		controlerManager.login(); //checks what to do on login after welcome screen
	}
	
	public void sendDefaultPlayersOptions() {
		getPackets().sendPlayerOption("Follow", 2, false);
		getPackets().sendPlayerOption("Trade with", 3, false);
		getPackets().sendPlayerOption("Req Assist", 4, false);
	}

	@Override
	public void checkMultiArea() {
		if(!started)
			return;
		boolean isAtMultiArea = World.isMultiArea(this);
		if(isAtMultiArea && !isAtMultiArea()) {
			setAtMultiArea(isAtMultiArea);
			getPackets().sendHideIComponent(745, 1, false);
		}else if (!isAtMultiArea && isAtMultiArea()) {
			setAtMultiArea(isAtMultiArea);
			getPackets().sendHideIComponent(745, 1, true);
		}
	}
	
	
	public void logout() {
		if(!running)
			return;
		getPackets().sendLogout();
		running = false;
	}
	//lets remove
	@Override
	public void finish() {
		if(hasFinished())
			return;
		controlerManager.logout(); //checks what to do on before logout for login
		running = false;
		setFinished(true);
		session.setDecoder(-1);
		SerializableFilesManager.savePlayer(this);
		World.updateEntityRegion(this);
		World.removePlayer(this);
		System.out.println("Finished Player: "+password+", name: "+username);
	}
	@Override
	public boolean restoreHitPoints() {
		boolean update = super.restoreHitPoints();
		if(update) {
			if(prayer.usingPrayer(0, 9))
				super.restoreHitPoints();
			if(resting)
				super.restoreHitPoints();
			refreshHitPoints();
		}
		return update;
	}

	public void refreshHitPoints() {
		getPackets().sendConfig(5000, getHitpoints());
		skills.refresh(Skills.HITPOINTS);
	}
	
	@Override
	public void removeHitpoints(Hit hit) {
		super.removeHitpoints(hit);
		refreshHitPoints();
	}

	@Override
	public int getMaxHitpoints() {
		return skills.getLevel(Skills.HITPOINTS)*10 + equipment.getEquipmentHpIncrease();
	}
	
	
	public String getUsername() {
		return username;
	}

	public String getPassword() {
		return password;
	}

	public void setRights(int rights) {
		this.rights = rights;
	}

	public int getRights() {
		return rights;
	}
	
	public WorldPacketsEncoder getPackets() {
		return session.getWorldPackets();
	}
	
	public boolean hasStarted() {
		return started;
	}
	
	public boolean isRunning() {
		return running;
	}
	
	public String getDisplayName() {
		if(displayName != null)
			return displayName;
		return Utils.formatPlayerNameForDisplay(username);
	}

	public boolean hasDisplayName() {
		return displayName != null;
	}
	public Appearence getAppearence() {
		return appearence;
	}

	public Equipment getEquipment() {
		return equipment;
	}
	
	public PublicChatMessage getNextPublicChatMessage() {
		return nextPublicChatMessage;
	}
	
	public void setNextPublicChatMessage(PublicChatMessage publicChatMessage) {
		this.nextPublicChatMessage = publicChatMessage;
	}
	
	public LocalPlayerUpdate getLocalPlayerUpdate() {
		return localPlayerUpdate;
	}
	
	public LocalNPCUpdate getLocalNPCUpdate() {
		return localNPCUpdate;
	}
	
	public int getDisplayMode() {
		return displayMode;
	}
	
	public InterfaceManager getInterfaceManager() {
		return interfaceManager;
	}

	public void setPacketsDecoderPing(long packetsDecoderPing) {
		this.packetsDecoderPing = packetsDecoderPing;
	}

	public long getPacketsDecoderPing() {
		return packetsDecoderPing;
	}
	
	public Session getSession() {
		return session;
	}

	public void setScreenWidth(int screenWidth) {
		this.screenWidth = screenWidth;
	}

	public int getScreenWidth() {
		return screenWidth;
	}

	public void setScreenHeight(int screenHeight) {
		this.screenHeight = screenHeight;
	}

	public int getScreenHeight() {
		return screenHeight;
	}
	
	public boolean clientHasLoadedMapRegion() {
		return clientLoadedMapRegion;
	}
	
	public void setClientHasLoadedMapRegion() {
		clientLoadedMapRegion = true;
	}
	
	public void setDisplayMode(int displayMode) {
		this.displayMode = displayMode;
	}
	
	public Inventory getInventory() {
		return inventory;
	}

	public Skills getSkills() {
		return skills;
	}

	public byte getRunEnergy() {
		return runEnergy;
	}

	public void drainRunEnergy() {
		setRunEnergy(runEnergy-1);
	}
	
	public void setRunEnergy(int runEnergy) {
		this.runEnergy = (byte) runEnergy;
		getPackets().sendRunEnergy();
	}

	public boolean isResting() {
		return resting;
	}

	public void setResting(boolean resting) {
		this.resting = resting;
		sendRunButtonConfig();
	}

	public SkillExecutor getSkillExecutor() {
		return skillExecutor;
	}

	public void setCoordsEvent(CoordsEvent coordsEvent) {
		this.coordsEvent = coordsEvent;
	}

	
	public ConcurrentHashMap<Object, Object> getTemporaryAttributtes() {
		return temporaryAttributes;
	}

	public DialogueManager getDialogueManager() {
		return dialogueManager;
	}
	
	public boolean getDontUpdateMyPlayer() {
		return dontUpdateMyPlayer;
	}

	public CombatDefinitions getCombatDefinitions() {
		return combatDefinitions;
	}

	

	@Override
	public double getMagePrayerMultiplier() {
		return 0.6;
	}

	@Override
	public double getRangePrayerMultiplier() {
		return 0.6;
	}

	@Override
	public double getMeleePrayerMultiplier() {
		return 0.6;
	}
	
	@Override
	public void handleIngoingHit(final Hit hit) {
		Entity source = hit.getSource();
		if(source == null || hit.getLook() == HitLook.Absorve_Hit || hit.getLook() == HitLook.Reflect_Hit)
			return;
		if(prayer.hasPrayersOn() && hit.getDamage() != 0) {
			if(hit.getLook() == HitLook.Mage_Hit) {
			if(prayer.usingPrayer(0, 17))
				hit.setDamage((int) (hit.getDamage()*source.getMagePrayerMultiplier()));
			else if (prayer.usingPrayer(1, 7)) {
				int deflectedDamage = (int) (hit.getDamage()*0.1);
				hit.setDamage((int) (hit.getDamage()*source.getMagePrayerMultiplier()));
				if(deflectedDamage > 0) {
					source.applyHit(new Hit(this, deflectedDamage, HitLook.Reflect_Hit));
					setNextGraphics(new Graphics(2228));
					setNextAnimation(new Animation(12573));
				}
			}
			} else if(hit.getLook() == HitLook.Range_Hit) {
			if(prayer.usingPrayer(0, 18))
				hit.setDamage((int) (hit.getDamage()*source.getRangePrayerMultiplier()));
			else if (prayer.usingPrayer(1, 8)) {
				int deflectedDamage = (int) (hit.getDamage()*0.1);
				hit.setDamage((int) (hit.getDamage()*source.getRangePrayerMultiplier()));
				if(deflectedDamage > 0) {
					source.applyHit(new Hit(this, deflectedDamage, HitLook.Reflect_Hit));
					setNextGraphics(new Graphics(2229));
					setNextAnimation(new Animation(12573));
				}
			}
			} else if(hit.getLook() == HitLook.Melee_Hit) {
			if(prayer.usingPrayer(0, 19))
				hit.setDamage((int) (hit.getDamage()*source.getMeleePrayerMultiplier()));
			else if (prayer.usingPrayer(1, 9)) {
				int deflectedDamage = (int) (hit.getDamage()*0.1);
				hit.setDamage((int) (hit.getDamage()*source.getMeleePrayerMultiplier()));
				if(deflectedDamage > 0) {
					source.applyHit(new Hit(this, deflectedDamage, HitLook.Reflect_Hit));
					setNextGraphics(new Graphics(2230));
					setNextAnimation(new Animation(12573));
				}
			}
			}
		}
		if(hit.getDamage() >= 200) {
			if(hit.getLook() == HitLook.Melee_Hit) {
				int reducedDamage = hit.getDamage() * combatDefinitions.getBonuses()[CombatDefinitions.ABSORVE_MELEE_BONUS] / 100;
				if(reducedDamage > 0) {
					hit.setDamage(hit.getDamage()-reducedDamage);
					applyHit(new Hit(source, reducedDamage, HitLook.Absorve_Hit));
				}
			}else if(hit.getLook() == HitLook.Range_Hit) {
				int reducedDamage = hit.getDamage() * combatDefinitions.getBonuses()[CombatDefinitions.ABSORVE_RANGE_BONUS] / 100;
				if(reducedDamage > 0) {
					hit.setDamage(hit.getDamage()-reducedDamage);
					applyHit(new Hit(source, reducedDamage, HitLook.Absorve_Hit));
				}
			}else if(hit.getLook() == HitLook.Range_Hit) {
				int reducedDamage = hit.getDamage() * combatDefinitions.getBonuses()[CombatDefinitions.ABSORVE_MAGE_BONUS] / 100;
				if(reducedDamage > 0) {
					hit.setDamage(hit.getDamage()-reducedDamage);
					applyHit(new Hit(source, reducedDamage, HitLook.Absorve_Hit));
				}
			}
		}
		if(source instanceof Player) {
			final Player p2 = (Player) source;
			if(p2.prayer.hasPrayersOn()) {
				if(p2.prayer.usingPrayer(0, 24)) { //smite
					int drain = hit.getDamage()/40;
					if(drain > 0)
						skills.drainPrayer(drain);
				}else {
					if(p2.prayer.usingPrayer(1, 18)) {
						final Player target = this;
						if(hit.getDamage() > 0)
							World.sendProjectile(p2, this, 2263, 11, 11 ,20, 5, 0, 0);
						p2.heal(hit.getDamage()/5);
						p2.getSkills().drainPrayer(hit.getDamage()/50);
						WorldTasksManager.schedule(new WorldTask() {
							@Override
							public void run() {
								setNextGraphics(new Graphics(2264));
								if(hit.getDamage() > 0)
									World.sendProjectile(target, p2, 2263, 11, 11 ,20, 5, 0, 0);
							}
						}, 1);
					}
					if(hit.getDamage() == 0)
						return;
					if(!p2.prayer.isBoostedLeech()) {
						if(hit.getLook() == HitLook.Melee_Hit) {
							if(p2.prayer.usingPrayer(1, 19)) {
								if(Utils.getRandom(4) == 0) {
									p2.prayer.increaseTurmoilBonus(this);
									p2.prayer.setBoostedLeech(true);
									return;
								}
							}else if(p2.prayer.usingPrayer(1, 1)) { //sap att
								if(Utils.getRandom(4) == 0) {
									if(p2.prayer.reachedMax(0)) {
										p2.getPackets().sendGameMessage("Your opponent has been weakened so much that your sap curse has no effect.", true);
									}else{
										p2.prayer.increaseLeechBonus(0);
										p2.getPackets().sendGameMessage("Your curse drains Attack from the enemy, boosting your Attack.", true);
									}
								p2.setNextAnimation(new Animation(12569));
								p2.setNextGraphics(new Graphics(2214));
								p2.prayer.setBoostedLeech(true);
								World.sendProjectile(p2, this, 2215, 35, 35 ,20, 5, 0, 0);
								WorldTasksManager.schedule(new WorldTask() {
									@Override
									public void run() {
										setNextGraphics(new Graphics(2216));
									}
								}, 1);
								return;
								}
							}else {
								if(p2.prayer.usingPrayer(1, 10)) {
									if(Utils.getRandom(7) == 0) {
										if(p2.prayer.reachedMax(3)) {
											p2.getPackets().sendGameMessage("Your opponent has been weakened so much that your leech curse has no effect.", true);
										}else{
											p2.prayer.increaseLeechBonus(3);
											p2.getPackets().sendGameMessage("Your curse drains Attack from the enemy, boosting your Attack.", true);
										}
										p2.setNextAnimation(new Animation(12575));
										p2.prayer.setBoostedLeech(true);
										World.sendProjectile(p2, this, 2231, 35, 35 ,20, 5, 0, 0);
										WorldTasksManager.schedule(new WorldTask() {
											@Override
											public void run() {
												setNextGraphics(new Graphics(2232));
											}
										}, 1);
										return;
									}
								}
								if(p2.prayer.usingPrayer(1, 14)) {
									if(Utils.getRandom(7) == 0) {
										if(p2.prayer.reachedMax(7)) {
											p2.getPackets().sendGameMessage("Your opponent has been weakened so much that your leech curse has no effect.", true);
										}else{
											p2.prayer.increaseLeechBonus(7);
											p2.getPackets().sendGameMessage("Your curse drains Strength from the enemy, boosting your Strength.", true);
										}
										p2.setNextAnimation(new Animation(12575));
										p2.prayer.setBoostedLeech(true);
										World.sendProjectile(p2, this, 2248, 35, 35 ,20, 5, 0, 0);
										WorldTasksManager.schedule(new WorldTask() {
											@Override
											public void run() {
												setNextGraphics(new Graphics(2250));
											}
										}, 1);
										return;
									}
								}
								
							}
							}
							if(hit.getLook() == HitLook.Range_Hit) {
								if(p2.prayer.usingPrayer(1, 2)) { //sap range
									if(Utils.getRandom(4) == 0) {
										if(p2.prayer.reachedMax(1)) {
											p2.getPackets().sendGameMessage("Your opponent has been weakened so much that your sap curse has no effect.", true);
										}else{
											p2.prayer.increaseLeechBonus(1);
											p2.getPackets().sendGameMessage("Your curse drains Range from the enemy, boosting your Range.", true);
										}
									p2.setNextAnimation(new Animation(12569));
									p2.setNextGraphics(new Graphics(2217));
									p2.prayer.setBoostedLeech(true);
									World.sendProjectile(p2, this, 2218, 35, 35 ,20, 5, 0, 0);
									WorldTasksManager.schedule(new WorldTask() {
										@Override
										public void run() {
											setNextGraphics(new Graphics(2219));
										}
									}, 1);
									return;
									}
								}else if(p2.prayer.usingPrayer(1, 11)) {
									if(Utils.getRandom(7) == 0) {
										if(p2.prayer.reachedMax(4)) {
											p2.getPackets().sendGameMessage("Your opponent has been weakened so much that your leech curse has no effect.", true);
										}else{
											p2.prayer.increaseLeechBonus(4);
											p2.getPackets().sendGameMessage("Your curse drains Range from the enemy, boosting your Range.", true);
										}
										p2.setNextAnimation(new Animation(12575));
										p2.prayer.setBoostedLeech(true);
										World.sendProjectile(p2, this, 2236, 35, 35 ,20, 5, 0, 0);
										WorldTasksManager.schedule(new WorldTask() {
											@Override
											public void run() {
												setNextGraphics(new Graphics(2238));
											}
										});
										return;
									}
								}
							}
							if(hit.getLook() == HitLook.Mage_Hit) {
								if(p2.prayer.usingPrayer(1, 3)) { //sap mage
									if(Utils.getRandom(4) == 0) {
									if(p2.prayer.reachedMax(2)) {
										p2.getPackets().sendGameMessage("Your opponent has been weakened so much that your sap curse has no effect.", true);
									}else{
										p2.prayer.increaseLeechBonus(2);
										p2.getPackets().sendGameMessage("Your curse drains Magic from the enemy, boosting your Magic.", true);
									}
									p2.setNextAnimation(new Animation(12569));
									p2.setNextGraphics(new Graphics(2220));
									p2.prayer.setBoostedLeech(true);
									World.sendProjectile(p2, this, 2221, 35, 35 ,20, 5, 0, 0);
									WorldTasksManager.schedule(new WorldTask() {
										@Override
										public void run() {
											setNextGraphics(new Graphics(2222));
										}
									}, 1);
									return;
									}
								}else if(p2.prayer.usingPrayer(1, 12)) {
									if(Utils.getRandom(7) == 0) {
										if(p2.prayer.reachedMax(5)) {
											p2.getPackets().sendGameMessage("Your opponent has been weakened so much that your leech curse has no effect.", true);
										}else{
											p2.prayer.increaseLeechBonus(5);
											p2.getPackets().sendGameMessage("Your curse drains Magic from the enemy, boosting your Magic.", true);
										}
										p2.setNextAnimation(new Animation(12575));
										p2.prayer.setBoostedLeech(true);
										World.sendProjectile(p2, this, 2240, 35, 35 ,20, 5, 0, 0);
										WorldTasksManager.schedule(new WorldTask() {
											@Override
											public void run() {
												setNextGraphics(new Graphics(2242));
											}
										}, 1);
										return;
									}
								}
							}
							
							//overall
							
							if(p2.prayer.usingPrayer(1, 13)) { //leech defence
								if(Utils.getRandom(10) == 0) {
									if(p2.prayer.reachedMax(6)) {
										p2.getPackets().sendGameMessage("Your opponent has been weakened so much that your leech curse has no effect.", true);
									}else{
										p2.prayer.increaseLeechBonus(6);
										p2.getPackets().sendGameMessage("Your curse drains Defence from the enemy, boosting your Defence.", true);
									}
									p2.setNextAnimation(new Animation(12575));
									p2.prayer.setBoostedLeech(true);
									World.sendProjectile(p2, this, 2244, 35, 35 ,20, 5, 0, 0);
									WorldTasksManager.schedule(new WorldTask() {
										@Override
										public void run() {
											setNextGraphics(new Graphics(2246));
										}
									}, 1);
									return;
								}
							}
							
							if(p2.prayer.usingPrayer(1, 15)) {
								if(Utils.getRandom(10) == 0) {
									if(getRunEnergy() <= 0) {
										p2.getPackets().sendGameMessage("Your opponent has been weakened so much that your leech curse has no effect.", true);
									}else{
										p2.setRunEnergy(p2.getRunEnergy() > 90 ? 100 : p2.getRunEnergy()+10);
										setRunEnergy(p2.getRunEnergy() > 10 ? getRunEnergy()-10 : 0);
									}
									p2.setNextAnimation(new Animation(12575));
									p2.prayer.setBoostedLeech(true);
									World.sendProjectile(p2, this, 2256, 35, 35 ,20, 5, 0, 0);
									WorldTasksManager.schedule(new WorldTask() {
										@Override
										public void run() {
											setNextGraphics(new Graphics(2258));
										}
									}, 1);
									return;
								}
							}
							
							if(p2.prayer.usingPrayer(1, 16)) {
								if(Utils.getRandom(10) == 0) {
									if(combatDefinitions.getSpecialAttackPercentage() <= 0) {
										p2.getPackets().sendGameMessage("Your opponent has been weakened so much that your leech curse has no effect.", true);
									}else{
										p2.combatDefinitions.restoreSpecialAttack();
										combatDefinitions.desecreaseSpecialAttack(10);
									}
									p2.setNextAnimation(new Animation(12575));
									p2.prayer.setBoostedLeech(true);
									World.sendProjectile(p2, this, 2252, 35, 35 ,20, 5, 0, 0);
									WorldTasksManager.schedule(new WorldTask() {
										@Override
										public void run() {
											setNextGraphics(new Graphics(2254));
										}
									}, 1);
									return;
								}
							}
							
							if(p2.prayer.usingPrayer(1, 4)) { //sap spec
								if(Utils.getRandom(10) == 0) {
								p2.setNextAnimation(new Animation(12569));
								p2.setNextGraphics(new Graphics(2223));
								p2.prayer.setBoostedLeech(true);
								if(combatDefinitions.getSpecialAttackPercentage() <= 0) {
									p2.getPackets().sendGameMessage("Your opponent has been weakened so much that your sap curse has no effect.", true);
								}else{
									combatDefinitions.desecreaseSpecialAttack(10);
								}
								World.sendProjectile(p2, this, 2224, 35, 35 ,20, 5, 0, 0);
								WorldTasksManager.schedule(new WorldTask() {
									@Override
									public void run() {
										setNextGraphics(new Graphics(2225));
									}
								}, 1);
								return;
								}
							}
					}
				}
			}
		}
 	}

	@Override
	public void sendDeath(final Entity source) {
		
		if(prayer.hasPrayersOn()) {
			if(prayer.usingPrayer(0, 22)) {
				setNextGraphics(new Graphics(437));
				final Player target = this;
				if(isAtMultiArea()) {
					for(int regionId : getMapRegionsIds()) {
						CopyOnWriteArrayList<Integer> playersIndexes = World.getRegion(regionId).getPlayerIndexes();
						if(playersIndexes != null) {
						for(int playerIndex : playersIndexes) {
							Player player = World.getPlayers().get(playerIndex);
							if(player == null
									|| !player.hasStarted()
									|| player.isDead()
									|| player.hasFinished()
									|| !player.withinDistance(this, 1)
									|| !target.getControlerManager().canHit(player))
								continue;
							player.applyHit(new Hit(target, Utils.getRandom((int) (skills.getLevelForXp(Skills.PRAYER) * 2.5)), HitLook.Hit));
						}
						}
						CopyOnWriteArrayList<Integer> npcsIndexes = World.getRegion(regionId).getNPCsIndexes();
						if(npcsIndexes != null) {
							for(int npcIndex : npcsIndexes) {
								NPC npc = World.getNPCs().get(npcIndex);
										if(npc == null
												|| npc.isDead()
												|| npc.hasFinished()
												|| !npc.withinDistance(this, 1)
											    || !npc.getDefinitions().hasAttackOption()
											    || !target.getControlerManager().canHit(npc))
											continue;
										npc.applyHit(new Hit(target, Utils.getRandom((int) (skills.getLevelForXp(Skills.PRAYER) * 2.5)), HitLook.Hit));	
							}
						}
					}
				}else{
					if(source != null && source != this 
							&& !source.isDead()
							&& !source.hasFinished()
							&& source.withinDistance(this, 1))
					source.applyHit(new Hit(target, Utils.getRandom((int) (skills.getLevelForXp(Skills.PRAYER) * 2.5)), HitLook.Hit));
				}
				WorldTasksManager.schedule(new WorldTask() {
					@Override
					public void run() {
						World.sendGraphics(target, new Graphics(438), new WorldTile(target.getX()-1, target.getY(), target.getPlane()));
						World.sendGraphics(target, new Graphics(438), new WorldTile(target.getX()+1, target.getY(), target.getPlane()));
						World.sendGraphics(target, new Graphics(438), new WorldTile(target.getX(), target.getY()-1, target.getPlane()));
						World.sendGraphics(target, new Graphics(438), new WorldTile(target.getX(), target.getY()+1, target.getPlane()));
						World.sendGraphics(target, new Graphics(438), new WorldTile(target.getX()-1, target.getY()-1, target.getPlane()));
						World.sendGraphics(target, new Graphics(438), new WorldTile(target.getX()-1, target.getY()+1, target.getPlane()));
						World.sendGraphics(target, new Graphics(438), new WorldTile(target.getX()+1, target.getY()-1, target.getPlane()));
						World.sendGraphics(target, new Graphics(438), new WorldTile(target.getX()+1, target.getY()+1, target.getPlane()));
					}
				});
			}else if (prayer.usingPrayer(1, 17)) {
				World.sendProjectile(this, new WorldTile(getX()+2, getY()+2, getPlane()), 2260, 24, 0, 41, 35, 30, 0);
				World.sendProjectile(this, new WorldTile(getX()+2, getY(), getPlane()), 2260, 41, 0, 41, 35, 30, 0);
				World.sendProjectile(this, new WorldTile(getX()+2, getY()-2, getPlane()), 2260, 41, 0, 41, 35, 30, 0);
				
				World.sendProjectile(this, new WorldTile(getX()-2, getY()+2, getPlane()), 2260, 41, 0, 41, 35, 30, 0);
				World.sendProjectile(this, new WorldTile(getX()-2, getY(), getPlane()), 2260, 41, 0, 41, 35, 30, 0);
				World.sendProjectile(this, new WorldTile(getX()-2, getY()-2, getPlane()), 2260, 41, 0, 41, 35, 30, 0);
				
				World.sendProjectile(this, new WorldTile(getX(), getY()+2, getPlane()), 2260, 41, 0, 41, 35, 30, 0);
				World.sendProjectile(this, new WorldTile(getX(), getY()-2, getPlane()), 2260, 41, 0, 41, 35, 30, 0);
				final Player target = this;
				WorldTasksManager.schedule(new WorldTask() {
					@Override
					public void run() {
						setNextGraphics(new Graphics(2259));
						
						
						if(isAtMultiArea()) {
							for(int regionId : getMapRegionsIds()) {
								CopyOnWriteArrayList<Integer> playersIndexes = World.getRegion(regionId).getPlayerIndexes();
								if(playersIndexes != null) {
								for(int playerIndex : playersIndexes) {
									Player player = World.getPlayers().get(playerIndex);
									if(player == null
											|| !player.hasStarted()
											|| player.isDead()
											|| player.hasFinished()
											|| !player.withinDistance(target, 2)
											|| !target.getControlerManager().canHit(player))
										continue;
									player.applyHit(new Hit(target, Utils.getRandom((int) (skills.getLevelForXp(Skills.PRAYER) * 3)), HitLook.Hit));
								}
								}
								CopyOnWriteArrayList<Integer> npcsIndexes = World.getRegion(regionId).getNPCsIndexes();
								if(npcsIndexes != null) {
									for(int npcIndex : npcsIndexes) {
										NPC npc = World.getNPCs().get(npcIndex);
												if(npc == null
														|| npc.isDead()
														|| npc.hasFinished()
														|| !npc.withinDistance(target, 2)
													    || !npc.getDefinitions().hasAttackOption()
													    || !target.getControlerManager().canHit(npc))
													continue;
												npc.applyHit(new Hit(target, Utils.getRandom((int) (skills.getLevelForXp(Skills.PRAYER) * 3)), HitLook.Hit));	
									}
								}
							}
						}else{
							if(source != null && source != target 
									&& !source.isDead()
									&& !source.hasFinished()
									&& source.withinDistance(target, 2))
							source.applyHit(new Hit(target, Utils.getRandom((int) (skills.getLevelForXp(Skills.PRAYER) * 3)), HitLook.Hit));
						}
						
						World.sendGraphics(target, new Graphics(2260), new WorldTile(getX()+2, getY()+2, getPlane()));
						World.sendGraphics(target, new Graphics(2260), new WorldTile(getX()+2, getY(), getPlane()));
						World.sendGraphics(target, new Graphics(2260), new WorldTile(getX()+2, getY()-2, getPlane()));
						
						World.sendGraphics(target, new Graphics(2260), new WorldTile(getX()-2, getY()+2, getPlane()));
						World.sendGraphics(target, new Graphics(2260), new WorldTile(getX()-2, getY(), getPlane()));
						World.sendGraphics(target, new Graphics(2260), new WorldTile(getX()-2, getY()-2, getPlane()));
						
						World.sendGraphics(target, new Graphics(2260), new WorldTile(getX(), getY()+2, getPlane()));
						World.sendGraphics(target, new Graphics(2260), new WorldTile(getX(), getY()-2, getPlane()));
						
						World.sendGraphics(target, new Graphics(2260), new WorldTile(getX()+1, getY()+1, getPlane()));
						World.sendGraphics(target, new Graphics(2260), new WorldTile(getX()+1, getY()-1, getPlane()));
						World.sendGraphics(target, new Graphics(2260), new WorldTile(getX()-1, getY()+1, getPlane()));
						World.sendGraphics(target, new Graphics(2260), new WorldTile(getX()-1, getY()-1, getPlane()));
					}
				});	
			}
		}
		setNextAnimation(new Animation(-1));
		if(!controlerManager.sendDeath())
			return;
		addStopDelay(7);
		WorldTasksManager.schedule(new WorldTask() {
			int loop;
			@Override
			public void run() {
				stopAll();
				if(loop == 0) {
					setNextAnimation(new Animation(836));
				}else if(loop == 1) {
					getPackets().sendGameMessage("Oh dear, you have died.");
				}else if(loop == 3) {
					reset();
					setNextWorldTile(new WorldTile(Settings.RESPAWN_PLAYER_LOCATION, 2));
					setNextAnimation(new Animation(-1));
					stop();
				}
				loop++;
			}
		}, 0, 1);
	}

	@Override
	public int getSize() {
		return appearence.getSize();
	}

	public boolean isCanPvp() {
		return canPvp;
	}

	public void setCanPvp(boolean canPvp) {
		this.canPvp = canPvp;
		getPackets().sendPlayerOption(canPvp ? "Attack" : "null", 1, true);
	}

	public Prayer getPrayer() {
		return prayer;
	}

	public long getStopDelay() {
		return stopDelay;
	}

	public void addStopDelay(long delay) {
		stopDelay = System.currentTimeMillis()+(delay*600);
	}
	
	public void useStairs(int emoteId, final WorldTile dest, int useDelay, int totalDelay) {
		useStairs(emoteId, dest, useDelay, totalDelay, null);
	}
	
	public void useStairs(int emoteId, final WorldTile dest, int useDelay, int totalDelay, final String message) {
		stopAll();
		addStopDelay(totalDelay);
		if(emoteId != -1)
			setNextAnimation(new Animation(emoteId));
		if(useDelay == 0)
			setNextWorldTile(dest);
		else {
			WorldTasksManager.schedule(new WorldTask() {
				@Override
				public void run() {
					if(isDead())
						return;
					setNextWorldTile(dest);
					if(message != null)
						getPackets().sendGameMessage(message);
				}
			}, useDelay-1);
		}
	}
	public Bank getBank() {
		return bank;
	}

	public int getMusicId() {
		return musicId;
	}

	public void setMusicId(int musicId) {
		this.musicId = musicId;
		musicDelay = System.currentTimeMillis();
		if(!started)
			return;
		getPackets().sendMusic(musicId);
		String musicName = Region.getMusicName(getRegionId());
		getPackets().sendIComponentText(187, 14, musicName == null ? "None" : musicName);
	}

	public long getMusicDelay() {
		return musicDelay;
	}

	public ForceMovement getNextForceMovement() {
		return nextForceMovement;
	}

	public void setNextForceMovement(ForceMovement nextForceMovement) {
		this.nextForceMovement = nextForceMovement;
	}

	public ControlerManager getControlerManager() {
		return controlerManager;
	}
	
	public void switchMouseButtons() {
		mouseButtons = !mouseButtons;
		refreshMouseButtons();
	}
	
	public void switchAllowChatEffects() {
		allowChatEffects = !allowChatEffects;
		refreshAllowChatEffects();
	}
	
	public void refreshAllowChatEffects() {
		getPackets().sendConfig(171, allowChatEffects ? 0 : 1);
	}
	
	public void refreshMouseButtons() {
		getPackets().sendConfig(170, mouseButtons ? 0 : 1);
	}

	public boolean isForceNextMapLoadRefresh() {
		return forceNextMapLoadRefresh;
	}

	public void setForceNextMapLoadRefresh(boolean forceNextMapLoadRefresh) {
		this.forceNextMapLoadRefresh = forceNextMapLoadRefresh;
	}
	



}
